/*
 * AIEngine a new generation network intrusion detection system.
 *
 * Copyright (C) 2013-2023  Luis Campo Giralte
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA  02110-1301, USA.
 *
 * Written by Luis Campo Giralte <luis.camp0.2009@gmail.com>
 *
 */
#ifndef SRC_NETLINK_H
#define SRC_NETLINK_H

#if defined(IS_LINUX)
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <random>

template <typename Protocol>
class NetlinkEndpoint
{
public:
	typedef Protocol protocol_type;
    	typedef boost::asio::detail::socket_addr_type data_type;
    
    	NetlinkEndpoint() {
    		std::random_device dev;
    		std::mt19937 rng(dev());
    		std::uniform_int_distribution<std::mt19937::result_type> dist6(1,9999999);

		sockaddr.nl_family = PF_NETLINK;
		sockaddr.nl_groups = RTMGRP_LINK;
		sockaddr.nl_pid = dist6(rng);	
    	}

    	NetlinkEndpoint(const NetlinkEndpoint& other) { sockaddr = other.sockaddr; }
    
    	NetlinkEndpoint& operator=(const NetlinkEndpoint& other) {
	
		sockaddr = other.sockaddr;
		return *this;
    	}
    
    	protocol_type protocol() const { return protocol_type(); }
    	data_type* data() { return reinterpret_cast<const struct sockaddr*>(&sockaddr); }
    	const data_type* data() const { return (struct sockaddr*)&sockaddr; }
    	std::size_t size() const { return sizeof(sockaddr); }
    	std::size_t capacity() const { return sizeof(sockaddr); }
    
    	friend bool operator==(const NetlinkEndpoint<Protocol>& e1, const NetlinkEndpoint<Protocol>& e2) {

		if (e1.nl_family != e2.nl_family) return false;
  		if (e1.nl_pid != e1.nl_pid) return false;

		return e1.nl_groups == e2.nl_groups;
    	}
    
    	friend bool operator<=(const NetlinkEndpoint<Protocol>& e1, const NetlinkEndpoint<Protocol>& e2) {
	
		return !(e2 < e1);
    	}
    
    	friend bool operator>=(const NetlinkEndpoint<Protocol>& e1, const NetlinkEndpoint<Protocol>& e2) {
	
		return !(e1 < e2);
    	}

private:
	sockaddr_nl sockaddr;
};


class NetlinkProtocol
{
public:
    	typedef NetlinkEndpoint<NetlinkProtocol> endpoint;
    	typedef boost::asio::basic_raw_socket<NetlinkProtocol> socket;

    	NetlinkProtocol() {}
    	NetlinkProtocol(int proto) { this->proto = proto; }

    	int type() const { return SOCK_RAW; }
    	int protocol() const { return proto; }
    	int family() const { return PF_NETLINK; }
    
private:
	int proto = 0; 
};

typedef boost::asio::basic_raw_socket<NetlinkProtocol> NetlinkSocket;

#endif // IS_LINUX

#endif
